<?php

namespace Divine\Http\Message;

class ResponseMessage extends AbstractMessage
{
    /**
     * The numeric HTTP response status code.
     *
     * @var int
     */
    protected $_statusCode;
    /**
     * The optional human readable reason phrase that follows the code.
     *
     * @var string
     */
    protected $_statusMessage;

    const CODE_BAD_REQUEST                     = 400;
    const CODE_UNAUTHORIZED                    = 401;
    const CODE_PAYMENT_REQUIRED                = 402;
    const CODE_FORBIDDEN                       = 403;
    const CODE_NOT_FOUND                       = 404;
    const CODE_METHOD_NOT_ALLOWED              = 405;
    const CODE_NOT_ACCEPTABLE                  = 406;
    const CODE_PROXY_AUTHENTICATION_REQUIRED   = 407;
    const CODE_REQUEST_TIMEOUT                 = 408;
    const CODE_CONFLICT                        = 409;
    const CODE_GONE                            = 410;
    const CODE_LENGTH_REQUIRED                 = 411;
    const CODE_PRECONDITION_FAILED             = 412;
    const CODE_REQUEST_ENTITY_TOO_LARGE        = 413;
    const CODE_REQUEST_URI_TOO_LONG            = 414;
    const CODE_UNSUPPORTED_MEDIA_TYPE          = 415;
    const CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    const CODE_EXPECTATION_FAILED              = 417;
    const CODE_INTERNAL_SERVER_ERROR           = 500;
    const CODE_NOT_IMPLEMENTED                 = 501;
    const CODE_BAD_GATEWAY                     = 502;
    const CODE_SERVICE_UNAVAILABLE             = 503;
    const CODE_GATEWAY_TIMEOUT                 = 504;
    const CODE_HTTP_VERSION_NOT_SUPPORTED      = 505;

    protected static $_statuses = array
    (
        self::CODE_BAD_REQUEST                     => 'Bad Request',
        self::CODE_UNAUTHORIZED                    => 'Unauthorized',
        self::CODE_PAYMENT_REQUIRED                => 'Payment Required',
        self::CODE_FORBIDDEN                       => 'Forbidden',
        self::CODE_NOT_FOUND                       => 'Not Found',
        self::CODE_METHOD_NOT_ALLOWED              => 'Method Not Allowed',
        self::CODE_NOT_ACCEPTABLE                  => 'Not Acceptable',
        self::CODE_PROXY_AUTHENTICATION_REQUIRED   => 'Proxy Authentication Required',
        self::CODE_REQUEST_TIMEOUT                 => 'Request Timeout',
        self::CODE_CONFLICT                        => 'Confict',
        self::CODE_GONE                            => 'Gone',
        self::CODE_LENGTH_REQUIRED                 => 'Length Required',
        self::CODE_PRECONDITION_FAILED             => 'Precondition Failed',
        self::CODE_REQUEST_ENTITY_TOO_LARGE        => 'Request Entity Too Large',
        self::CODE_REQUEST_URI_TOO_LONG            => 'Request-URI Too Long',
        self::CODE_UNSUPPORTED_MEDIA_TYPE          => 'Unsupported Media Type',
        self::CODE_REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable',
        self::CODE_EXPECTATION_FAILED              => 'Expectation Failed',
        self::CODE_INTERNAL_SERVER_ERROR           => 'Internal Server Error',
        self::CODE_NOT_IMPLEMENTED                 => 'Not Implemented',
        self::CODE_BAD_GATEWAY                     => 'Bad Gateway',
        self::CODE_SERVICE_UNAVAILABLE             => 'Service Unavailable',
        self::CODE_GATEWAY_TIMEOUT                 => 'Gateway Timeout',
        self::CODE_HTTP_VERSION_NOT_SUPPORTED      => 'HTTP Version Not Supported'
    );

    /**
     * Creates a new Divine\Http\Message\ResponseMessage instance.
     *
     * @param string $httpVersion The HTTP Version of the message.
     * @param int $statusCode The numeric HTTP response status code.
     * @param string $statusMessage The optional HTTP response status reason phrase that follows the status code.
     * @param array $headers HTTP headers as an array where the index is the field name.
     * @param string $body An optional message body.
     */
    public function __construct($httpVersion, $statusCode, $statusMessage = NULL, array $headers = array(), $body = NULL)
    {
        $this->_setHttpVersion($httpVersion);
        $this->_setStatusCode($statusCode);

        if (is_null($statusMessage))
        {
            $statusMessage = static::_getStatusMessageFromStatusCode($statusCode);
        }

        $this->_setStatusMessage($statusMessage);
        $this->_setHeaders($headers);
        $this->_setBody($body);
    }

    /**
     * Set the numeric HTTP status code.
     *
     * @param int $statusCode
     * @throws \UnexpectedValueException Thrown when an unknown value is passed for the HTTP response status code.
     */
    protected function _setStatusCode($statusCode)
    {
        if (!static::_isValidStatusCode($statusCode))
        {
            throw new \UnexpectedValueException('Unknown HTTP response status code: ' . $statusCode);
        }

        $this->_statusCode = $statusCode;
    }

    /**
     * Set the optional HTTP reason phrase.
     *
     * @param string $statusMessage
     */
    protected function _setStatusMessage($statusMessage)
    {
        $this->_statusMessage = $statusMessage;
    }

    /**
     * Returns the response status code.
     *
     * @return int
     */
    public function getStatusCode()
    {
        return $this->_statusCode;
    }

    /**
     * Returns the response status message that follows the status code.
     *
     * @return string
     */
    public function getStatusMessage()
    {
        return $this->_statusMessage;
    }

    /**
     * Checks for a valid numeric HTTP response status code.
     *
     * @param int $statusCode A numeric HTTP response status code.
     * @return boolean
     */
    protected static function _isValidStatusCode($statusCode)
    {
        return is_int($statusCode) && array_key_exists($statusCode, self::$_statuses);
    }

    /**
     * Retrieves a default HTTP response status reason phrase for a given numeric HTTP response status code.
     *
     * @param int $statusCode A numeric HTTP response status code.
     * @return string
     */
    protected static function _getStatusMessageFromStatusCode($statusCode)
    {
        return static::$_statuses[$statusCode];
    }
}